Цели исследования:
Определить конверсию в покупку
Задачи:
Определить эффективно ли изменение шрифта в приложении
Задачи:
Так как качество данных неизвестно, то необходимо также провести предобработку данных.
Ход исследования:
#Импорт необходимых библиотек
import pandas as pd
import datetime as dt
from datetime import datetime
import numpy as np
import matplotlib.pyplot as plt
#from pandas.plotting import register_matplotlib_converters
import warnings
warnings.filterwarnings('ignore')
import seaborn as sns
import scipy.stats as stats
from scipy import stats as st
import math as mth
#чтение файла
try:
data = pd.read_csv('...', sep='\t')
except FileNotFoundError:
print('Укажи верный путь к файлу')
#вывод первых строк
data.head(10)
| EventName | DeviceIDHash | EventTimestamp | ExpId | |
|---|---|---|---|---|
| 0 | MainScreenAppear | 4575588528974610257 | 1564029816 | 246 |
| 1 | MainScreenAppear | 7416695313311560658 | 1564053102 | 246 |
| 2 | PaymentScreenSuccessful | 3518123091307005509 | 1564054127 | 248 |
| 3 | CartScreenAppear | 3518123091307005509 | 1564054127 | 248 |
| 4 | PaymentScreenSuccessful | 6217807653094995999 | 1564055322 | 248 |
| 5 | CartScreenAppear | 6217807653094995999 | 1564055323 | 248 |
| 6 | OffersScreenAppear | 8351860793733343758 | 1564066242 | 246 |
| 7 | MainScreenAppear | 5682100281902512875 | 1564085677 | 246 |
| 8 | MainScreenAppear | 1850981295691852772 | 1564086702 | 247 |
| 9 | MainScreenAppear | 5407636962369102641 | 1564112112 | 246 |
#основная информация о данных
data.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 244126 entries, 0 to 244125 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 EventName 244126 non-null object 1 DeviceIDHash 244126 non-null int64 2 EventTimestamp 244126 non-null int64 3 ExpId 244126 non-null int64 dtypes: int64(3), object(1) memory usage: 7.5+ MB
# проверка на явные дубликаты
data.duplicated().sum()
413
pers = data.duplicated().sum()/data['EventName'].count()
f'Процент дубликатов в датасета: {pers:.3%}'
'Процент дубликатов в датасета: 0.169%'
Вывод по шагу 1:
data = data.drop_duplicates()
data = data.reset_index( drop = True )
data
| EventName | DeviceIDHash | EventTimestamp | ExpId | |
|---|---|---|---|---|
| 0 | MainScreenAppear | 4575588528974610257 | 1564029816 | 246 |
| 1 | MainScreenAppear | 7416695313311560658 | 1564053102 | 246 |
| 2 | PaymentScreenSuccessful | 3518123091307005509 | 1564054127 | 248 |
| 3 | CartScreenAppear | 3518123091307005509 | 1564054127 | 248 |
| 4 | PaymentScreenSuccessful | 6217807653094995999 | 1564055322 | 248 |
| ... | ... | ... | ... | ... |
| 243708 | MainScreenAppear | 4599628364049201812 | 1565212345 | 247 |
| 243709 | MainScreenAppear | 5849806612437486590 | 1565212439 | 246 |
| 243710 | MainScreenAppear | 5746969938801999050 | 1565212483 | 246 |
| 243711 | MainScreenAppear | 5746969938801999050 | 1565212498 | 246 |
| 243712 | OffersScreenAppear | 5746969938801999050 | 1565212517 | 246 |
243713 rows × 4 columns
data.columns = ['event', 'device_id', 'event_timestamp', 'exp_id']
data.head(1)
| event | device_id | event_timestamp | exp_id | |
|---|---|---|---|---|
| 0 | MainScreenAppear | 4575588528974610257 | 1564029816 | 246 |
Пропуски в датасете отсутствуют.
Все типы данные соответствуют отображаемым данным за исключением даты, которую исправим на следующем шаге. Дату необходимо привести к типу datetime, а также имеет смысл выделить отдельный столбец с датой без времени.
# создаем столбец с датой и временем
data['event_time'] = pd.to_datetime(data['event_timestamp'], unit = 's')
#создаем столбец с датой
data['event_date'] = pd.to_datetime(data['event_time']).dt.date
#исключаем столбец с исходной датой в секундах
data = data.drop(columns = ['event_timestamp'],axis = 1)
data
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 0 | MainScreenAppear | 4575588528974610257 | 246 | 2019-07-25 04:43:36 | 2019-07-25 |
| 1 | MainScreenAppear | 7416695313311560658 | 246 | 2019-07-25 11:11:42 | 2019-07-25 |
| 2 | PaymentScreenSuccessful | 3518123091307005509 | 248 | 2019-07-25 11:28:47 | 2019-07-25 |
| 3 | CartScreenAppear | 3518123091307005509 | 248 | 2019-07-25 11:28:47 | 2019-07-25 |
| 4 | PaymentScreenSuccessful | 6217807653094995999 | 248 | 2019-07-25 11:48:42 | 2019-07-25 |
| ... | ... | ... | ... | ... | ... |
| 243708 | MainScreenAppear | 4599628364049201812 | 247 | 2019-08-07 21:12:25 | 2019-08-07 |
| 243709 | MainScreenAppear | 5849806612437486590 | 246 | 2019-08-07 21:13:59 | 2019-08-07 |
| 243710 | MainScreenAppear | 5746969938801999050 | 246 | 2019-08-07 21:14:43 | 2019-08-07 |
| 243711 | MainScreenAppear | 5746969938801999050 | 246 | 2019-08-07 21:14:58 | 2019-08-07 |
| 243712 | OffersScreenAppear | 5746969938801999050 | 246 | 2019-08-07 21:15:17 | 2019-08-07 |
243713 rows × 5 columns
Вывод по шагу 2:
#Расчет общего числа событий
event_nmb = data['event'].count()
f'Кoличество событий равно количеству записей в датасете и равно: {event_nmb}'
'Кoличество событий равно количеству записей в датасете и равно: 243713'
#Расчет количества пользователей
users_nmb = data['device_id'].nunique()
f'Кoличество уникальных пользователей в логе: {users_nmb}'
'Кoличество уникальных пользователей в логе: 7551'
# расчет среднего
events_per_user = event_nmb/users_nmb
f'В среднем на одного пользователя приходится {events_per_user:.0f} события'
'В среднем на одного пользователя приходится 32 события'
Вывод: на одного пользователя в среднем приходится 32 события. Всего в данных 7551 пользователь и совершилось 273 713 событий.
#вывод минимальной даты
mindate = data['event_date'].min()
print(mindate)
2019-07-25
#вывод максимальной даты
maxdate = data['event_date'].max()
print(maxdate)
2019-08-07
# построекние гистограмы числа событий по дате и времени
data['event_time'].hist(bins=120, figsize = (15,5))
plt.title('Распределение экспериментов по времени');
#очищаем данные, убрав более старые данные
data_cleaned = data[data['event_time'] >= '2019-08-01']
data_cleaned.head()
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 2826 | Tutorial | 3737462046622621720 | 246 | 2019-08-01 00:07:28 | 2019-08-01 |
| 2827 | MainScreenAppear | 3737462046622621720 | 246 | 2019-08-01 00:08:00 | 2019-08-01 |
| 2828 | MainScreenAppear | 3737462046622621720 | 246 | 2019-08-01 00:08:55 | 2019-08-01 |
| 2829 | OffersScreenAppear | 3737462046622621720 | 246 | 2019-08-01 00:08:58 | 2019-08-01 |
| 2830 | MainScreenAppear | 1433840883824088890 | 247 | 2019-08-01 00:08:59 | 2019-08-01 |
#проверяем наличие всех груп и их сбаллансированность
users_A1 = data_cleaned[data_cleaned['exp_id']==246]['device_id'].nunique()
users_A2 = data_cleaned[data_cleaned['exp_id']==247]['device_id'].nunique()
users_B = data_cleaned[data_cleaned['exp_id']==248]['device_id'].nunique()
print(f'Количество пользователей в группе А (246):{users_A1}')
print(f'Количество пользователей в группе А (247):{users_A2}')
print(f'Количество пользователей в группе B (248):{users_B}')
Количество пользователей в группе А (246):2484 Количество пользователей в группе А (247):2513 Количество пользователей в группе B (248):2537
#проверим нет ли пересекающихся пользователей
data_cleaned.groupby('device_id').agg({'exp_id':'nunique'}).query('exp_id > 1').count()
exp_id 0 dtype: int64
# проверим какой процент событий мы потеряли при отсечении дат
data_events = data.groupby('event').agg({'event':'count'}).rename(columns = {'event':'events_amt'}).sort_values(by='events_amt', ascending = False).reset_index()
data_events_cleaned = data_cleaned.groupby('event').agg({'event':'count'}).rename(columns = {'event':'events_amt_cleaned'}).sort_values(by='events_amt_cleaned', ascending = False).reset_index()
data_events = data_events.merge(data_events_cleaned, on ='event')
#добавим строку с суммой
total = {'event':'total', 'events_amt': data_events['events_amt'].sum(), 'events_amt_cleaned':data_events['events_amt_cleaned'].sum()}
data_events = data_events.append(total, ignore_index = True)
#добавим столбец с процентом отклонения
data_events['percentage_lost,%'] = round((1 - data_events['events_amt_cleaned']/data_events['events_amt'])*100, 2)
data_events
| event | events_amt | events_amt_cleaned | percentage_lost,% | |
|---|---|---|---|---|
| 0 | MainScreenAppear | 119101 | 117328 | 1.49 |
| 1 | OffersScreenAppear | 46808 | 46333 | 1.01 |
| 2 | CartScreenAppear | 42668 | 42303 | 0.86 |
| 3 | PaymentScreenSuccessful | 34118 | 33918 | 0.59 |
| 4 | Tutorial | 1018 | 1005 | 1.28 |
| 5 | total | 243713 | 240887 | 1.16 |
# проверим какой процент пользователей мы потеряли при отсечении дат
data_users = data.groupby('exp_id').agg({'device_id':'nunique'}).rename(columns = {'device_id':'users_amt'}).sort_values(by='exp_id').reset_index()
data_users_cleaned = data_cleaned.groupby('exp_id').agg({'device_id':'nunique'}).rename(columns = {'device_id':'users_amt_cleaned'}).sort_values(by='exp_id').reset_index()
data_users = data_users.merge(data_users_cleaned, on ='exp_id')
#добавим строку с суммой
total = {'exp_id':'total', 'users_amt': data_users['users_amt'].sum(), 'users_amt_cleaned':data_users['users_amt_cleaned'].sum()}
data_users = data_users.append(total, ignore_index = True)
#добавим столбец с процентом отклонения
data_users['percentage_lost,%'] = round((1 - data_users['users_amt_cleaned']/data_users['users_amt'])*100, 2)
data_users
| exp_id | users_amt | users_amt_cleaned | percentage_lost,% | |
|---|---|---|---|---|
| 0 | 246 | 2489 | 2484 | 0.20 |
| 1 | 247 | 2520 | 2513 | 0.28 |
| 2 | 248 | 2542 | 2537 | 0.20 |
| 3 | total | 7551 | 7534 | 0.23 |
Выводы по шагу 3:
# расчет сколько раз совершалось каждое событие
events_amt = data_cleaned.groupby('event').agg({'event':'count'}).rename(columns = {'event':'events_amt'}).sort_values(by='events_amt', ascending = False)
events_amt
| events_amt | |
|---|---|
| event | |
| MainScreenAppear | 117328 |
| OffersScreenAppear | 46333 |
| CartScreenAppear | 42303 |
| PaymentScreenSuccessful | 33918 |
| Tutorial | 1005 |
# расчет числа пользователей, которые совершили каждое событие
users_event_amt = data_cleaned.groupby('event').agg({'device_id':'nunique'}).rename(columns = {'device_id':'users_amt'}).sort_values(by='users_amt', ascending = False)
users_event_amt
| users_amt | |
|---|---|
| event | |
| MainScreenAppear | 7419 |
| OffersScreenAppear | 4593 |
| CartScreenAppear | 3734 |
| PaymentScreenSuccessful | 3539 |
| Tutorial | 840 |
#добавим к таблице % от общего числа пользователей
users_event_amt['percentage_of_total_users'] = round(users_event_amt['users_amt']/data_cleaned['device_id'].nunique()*100,2)
users_event_amt.reset_index(inplace=True)
users_event_amt
| event | users_amt | percentage_of_total_users | |
|---|---|---|---|
| 0 | MainScreenAppear | 7419 | 98.47 |
| 1 | OffersScreenAppear | 4593 | 60.96 |
| 2 | CartScreenAppear | 3734 | 49.56 |
| 3 | PaymentScreenSuccessful | 3539 | 46.97 |
| 4 | Tutorial | 840 | 11.15 |
Предположение о последовательности событий:
Для дальнейшего исследования воронки исключим из нее событие Tutorial.
#удаление строки
users_event_amt_cleaned = users_event_amt[users_event_amt['event'] != 'Tutorial']
users_event_amt_cleaned
| event | users_amt | percentage_of_total_users | |
|---|---|---|---|
| 0 | MainScreenAppear | 7419 | 98.47 |
| 1 | OffersScreenAppear | 4593 | 60.96 |
| 2 | CartScreenAppear | 3734 | 49.56 |
| 3 | PaymentScreenSuccessful | 3539 | 46.97 |
#для расчета процента перехода пользователей добавим столбец со смещенными users_amt
users_event_amt_cleaned['users_amt_shift'] = users_event_amt_cleaned['users_amt'].shift(1, fill_value=0)
#расчет прохождения на следующий этап воронки
users_event_amt_cleaned['convertion'] = round(users_event_amt_cleaned['users_amt']/users_event_amt_cleaned['users_amt_shift']*100,2)
#удалим дополнительный столбец
users_event_amt_cleaned = users_event_amt_cleaned.drop(columns = ['users_amt_shift'],axis = 1)
#добавим столбец с долей пользователей относительно 1ого шага
users_event_amt_cleaned['percentage_of_1st_event'] = (
round(users_event_amt_cleaned['users_amt']/
users_event_amt_cleaned['users_amt'].values [0]*100,2)
)
users_event_amt_cleaned
| event | users_amt | percentage_of_total_users | convertion | percentage_of_1st_event | |
|---|---|---|---|---|---|
| 0 | MainScreenAppear | 7419 | 98.47 | inf | 100.00 |
| 1 | OffersScreenAppear | 4593 | 60.96 | 61.91 | 61.91 |
| 2 | CartScreenAppear | 3734 | 49.56 | 81.30 | 50.33 |
| 3 | PaymentScreenSuccessful | 3539 | 46.97 | 94.78 | 47.70 |
#построим воронку конверсии пользователей
from plotly import graph_objects as go
fig = go.Figure(go.Funnel(
y = users_event_amt_cleaned['event'],
x = users_event_amt_cleaned['users_amt'],
textposition = "inside",
textinfo = "value+percent initial",
marker = {"color": ['powderblue','lightsteelblue', 'mediumslateblue','midnightblue']}
)
)
fig.update_layout(title_text='Воронка событий (с учетом процента перехода пользователей на следующий шаг)')
fig.show()
Выводы по шагу 4:
# напомним количество пользователей в каждой группе с учетом очистки событи Tutorial
data_cleaned = data_cleaned[data_cleaned['event'] != 'Tutorial']
users_A1 = data_cleaned[data_cleaned['exp_id']==246]['device_id'].nunique()
users_A2 = data_cleaned[data_cleaned['exp_id']==247]['device_id'].nunique()
users_B = data_cleaned[data_cleaned['exp_id']==248]['device_id'].nunique()
print(f'Количество пользователей в 1ой группе А (246):{users_A1}')
print(f'Количество пользователей во 2ой группе А (247):{users_A2}')
print(f'Количество пользователей в группе B (248):{users_B}')
Количество пользователей в 1ой группе А (246):2483 Количество пользователей во 2ой группе А (247):2512 Количество пользователей в группе B (248):2535
# Создадим датасеты по группам
dataA1 = data_cleaned[data_cleaned['exp_id']==246]
dataA2 = data_cleaned[data_cleaned['exp_id']==247]
dataB = data_cleaned[data_cleaned['exp_id']==248]
# обобщенная группа А
dataA = data_cleaned[data_cleaned['exp_id']!=248]
display(dataA1.head())
display(dataA2.head())
display(dataB.head())
dataA.head()
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 2827 | MainScreenAppear | 3737462046622621720 | 246 | 2019-08-01 00:08:00 | 2019-08-01 |
| 2828 | MainScreenAppear | 3737462046622621720 | 246 | 2019-08-01 00:08:55 | 2019-08-01 |
| 2829 | OffersScreenAppear | 3737462046622621720 | 246 | 2019-08-01 00:08:58 | 2019-08-01 |
| 2832 | OffersScreenAppear | 3737462046622621720 | 246 | 2019-08-01 00:10:26 | 2019-08-01 |
| 2833 | MainScreenAppear | 3737462046622621720 | 246 | 2019-08-01 00:10:47 | 2019-08-01 |
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 2830 | MainScreenAppear | 1433840883824088890 | 247 | 2019-08-01 00:08:59 | 2019-08-01 |
| 2831 | MainScreenAppear | 4899590676214355127 | 247 | 2019-08-01 00:10:15 | 2019-08-01 |
| 2836 | MainScreenAppear | 4899590676214355127 | 247 | 2019-08-01 00:11:28 | 2019-08-01 |
| 2837 | OffersScreenAppear | 4899590676214355127 | 247 | 2019-08-01 00:11:30 | 2019-08-01 |
| 2841 | OffersScreenAppear | 4899590676214355127 | 247 | 2019-08-01 00:12:36 | 2019-08-01 |
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 2842 | MainScreenAppear | 4613461174774205834 | 248 | 2019-08-01 00:14:31 | 2019-08-01 |
| 2843 | MainScreenAppear | 4613461174774205834 | 248 | 2019-08-01 00:14:34 | 2019-08-01 |
| 2844 | CartScreenAppear | 4613461174774205834 | 248 | 2019-08-01 00:14:34 | 2019-08-01 |
| 2845 | PaymentScreenSuccessful | 4613461174774205834 | 248 | 2019-08-01 00:14:43 | 2019-08-01 |
| 2846 | OffersScreenAppear | 4613461174774205834 | 248 | 2019-08-01 00:14:51 | 2019-08-01 |
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 2827 | MainScreenAppear | 3737462046622621720 | 246 | 2019-08-01 00:08:00 | 2019-08-01 |
| 2828 | MainScreenAppear | 3737462046622621720 | 246 | 2019-08-01 00:08:55 | 2019-08-01 |
| 2829 | OffersScreenAppear | 3737462046622621720 | 246 | 2019-08-01 00:08:58 | 2019-08-01 |
| 2830 | MainScreenAppear | 1433840883824088890 | 247 | 2019-08-01 00:08:59 | 2019-08-01 |
| 2831 | MainScreenAppear | 4899590676214355127 | 247 | 2019-08-01 00:10:15 | 2019-08-01 |
# создадим датасеты по группам и событиям
def data_cleaned_by_event(data):
data_MSA = data[data['event']=='MainScreenAppear']
data_OSA = data[data['event']=='OffersScreenAppear']
data_CSA = data[data['event']=='CartScreenAppear']
data_PSS = data[data['event']=='PaymentScreenSuccessful']
display(data_MSA.head(2))
display(data_OSA.head(2))
display(data_CSA.head(2))
display(data_PSS.head(2))
return data_MSA, data_OSA, data_CSA, data_PSS
dataA1MSA, dataA1OSA, dataA1CSA, dataA1PSS = data_cleaned_by_event(dataA1)
dataA2MSA, dataA2OSA, dataA2CSA, dataA2PSS = data_cleaned_by_event(dataA2)
dataBMSA, dataBOSA, dataBCSA, dataBPSS = data_cleaned_by_event(dataB)
dataAMSA, dataAOSA, dataACSA, dataAPSS = data_cleaned_by_event(dataA)
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 2827 | MainScreenAppear | 3737462046622621720 | 246 | 2019-08-01 00:08:00 | 2019-08-01 |
| 2828 | MainScreenAppear | 3737462046622621720 | 246 | 2019-08-01 00:08:55 | 2019-08-01 |
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 2829 | OffersScreenAppear | 3737462046622621720 | 246 | 2019-08-01 00:08:58 | 2019-08-01 |
| 2832 | OffersScreenAppear | 3737462046622621720 | 246 | 2019-08-01 00:10:26 | 2019-08-01 |
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 2991 | CartScreenAppear | 5653442602434498252 | 246 | 2019-08-01 00:52:53 | 2019-08-01 |
| 2998 | CartScreenAppear | 5653442602434498252 | 246 | 2019-08-01 00:56:16 | 2019-08-01 |
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 2990 | PaymentScreenSuccessful | 5653442602434498252 | 246 | 2019-08-01 00:52:53 | 2019-08-01 |
| 2997 | PaymentScreenSuccessful | 5653442602434498252 | 246 | 2019-08-01 00:56:13 | 2019-08-01 |
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 2830 | MainScreenAppear | 1433840883824088890 | 247 | 2019-08-01 00:08:59 | 2019-08-01 |
| 2831 | MainScreenAppear | 4899590676214355127 | 247 | 2019-08-01 00:10:15 | 2019-08-01 |
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 2837 | OffersScreenAppear | 4899590676214355127 | 247 | 2019-08-01 00:11:30 | 2019-08-01 |
| 2841 | OffersScreenAppear | 4899590676214355127 | 247 | 2019-08-01 00:12:36 | 2019-08-01 |
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 2862 | CartScreenAppear | 2712290788139738557 | 247 | 2019-08-01 00:22:45 | 2019-08-01 |
| 2866 | CartScreenAppear | 2712290788139738557 | 247 | 2019-08-01 00:23:09 | 2019-08-01 |
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 2861 | PaymentScreenSuccessful | 2712290788139738557 | 247 | 2019-08-01 00:22:44 | 2019-08-01 |
| 2865 | PaymentScreenSuccessful | 2712290788139738557 | 247 | 2019-08-01 00:23:08 | 2019-08-01 |
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 2842 | MainScreenAppear | 4613461174774205834 | 248 | 2019-08-01 00:14:31 | 2019-08-01 |
| 2843 | MainScreenAppear | 4613461174774205834 | 248 | 2019-08-01 00:14:34 | 2019-08-01 |
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 2846 | OffersScreenAppear | 4613461174774205834 | 248 | 2019-08-01 00:14:51 | 2019-08-01 |
| 2851 | OffersScreenAppear | 6121366368901703338 | 248 | 2019-08-01 00:15:54 | 2019-08-01 |
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 2844 | CartScreenAppear | 4613461174774205834 | 248 | 2019-08-01 00:14:34 | 2019-08-01 |
| 3062 | CartScreenAppear | 1694940645335807244 | 248 | 2019-08-01 01:13:06 | 2019-08-01 |
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 2845 | PaymentScreenSuccessful | 4613461174774205834 | 248 | 2019-08-01 00:14:43 | 2019-08-01 |
| 3064 | PaymentScreenSuccessful | 1694940645335807244 | 248 | 2019-08-01 01:13:12 | 2019-08-01 |
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 2827 | MainScreenAppear | 3737462046622621720 | 246 | 2019-08-01 00:08:00 | 2019-08-01 |
| 2828 | MainScreenAppear | 3737462046622621720 | 246 | 2019-08-01 00:08:55 | 2019-08-01 |
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 2829 | OffersScreenAppear | 3737462046622621720 | 246 | 2019-08-01 00:08:58 | 2019-08-01 |
| 2832 | OffersScreenAppear | 3737462046622621720 | 246 | 2019-08-01 00:10:26 | 2019-08-01 |
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 2862 | CartScreenAppear | 2712290788139738557 | 247 | 2019-08-01 00:22:45 | 2019-08-01 |
| 2866 | CartScreenAppear | 2712290788139738557 | 247 | 2019-08-01 00:23:09 | 2019-08-01 |
| event | device_id | exp_id | event_time | event_date | |
|---|---|---|---|---|---|
| 2861 | PaymentScreenSuccessful | 2712290788139738557 | 247 | 2019-08-01 00:22:44 | 2019-08-01 |
| 2865 | PaymentScreenSuccessful | 2712290788139738557 | 247 | 2019-08-01 00:23:08 | 2019-08-01 |
Для проверки статистической разницы между всеми группами необходимо провести в общей сложности 16 тестов:
Для всех тестов будут приняты следующие гипотезы:
def statistic_check(data_total_1,data_total_2, data_success_1, data_success_2, alpha):
customers = np.array([data_total_1['device_id'].nunique(), data_total_2['device_id'].nunique()])
success = np.array([data_success_1['device_id'].nunique(), data_success_2['device_id'].nunique()])
p1 = success[0]/customers[0]
p2 = success[1]/customers[1]
p_combined = (success[0] + success[1]) / (customers[0] + customers[1])
difference = p1 - p2
z_value = difference / mth.sqrt(p_combined * (1 - p_combined) * (1/customers[0] + 1/customers[1]))
distr = st.norm(0, 1)
p_value = (1 - distr.cdf(abs(z_value))) * 2
print('p-значение: ', round(p_value,2))
if p_value < alpha:
print('Отвергаем нулевую гипотезу: между группами есть значимая разница')
else:
print('Не получилось отвергнуть нулевую гипотезу, нет оснований считать группы пользователей разными')
В данном исследовании мы проводим множественные сравнение. То есть повышается вероятность появления ошибки первого роды. Для снижения вероятности групповой ошибки 1ого рода необходимо применить поправку.
Самой распространенной является поправка Бонферрони, ее и используем в данном исследовании.
Но надо отметить, что если будет нужно повысить мощность теста, сохраняя FWER < ɑ, можно будет применить методы Холма и Шидака.
alpha = 0.05
alpha_bonferroni = alpha/16
alpha_bonferroni
0.003125
statistic_check(dataA1, dataA2, dataA1MSA, dataA2MSA, alpha_bonferroni)
p-значение: 0.75 Не получилось отвергнуть нулевую гипотезу, нет оснований считать группы пользователей разными
statistic_check(dataA1, dataA2, dataA1OSA, dataA2OSA, alpha_bonferroni)
p-значение: 0.25 Не получилось отвергнуть нулевую гипотезу, нет оснований считать группы пользователей разными
statistic_check(dataA1, dataA2, dataA1CSA, dataA2CSA, alpha_bonferroni)
p-значение: 0.23 Не получилось отвергнуть нулевую гипотезу, нет оснований считать группы пользователей разными
statistic_check(dataA1, dataA2, dataA1PSS, dataA2PSS, alpha_bonferroni)
p-значение: 0.11 Не получилось отвергнуть нулевую гипотезу, нет оснований считать группы пользователей разными
Вывод:
во всех четырех группах по событиям была отвергнута нулевая гипотеза, значит у нас нет оснований, считать, что между группами А есть значимая разница.
statistic_check(dataA1, dataB, dataA1MSA, dataBMSA, alpha_bonferroni)
p-значение: 0.34 Не получилось отвергнуть нулевую гипотезу, нет оснований считать группы пользователей разными
statistic_check(dataA1, dataB, dataA1OSA, dataBOSA, alpha_bonferroni)
p-значение: 0.21 Не получилось отвергнуть нулевую гипотезу, нет оснований считать группы пользователей разными
statistic_check(dataA1, dataB, dataA1CSA, dataBCSA, alpha_bonferroni)
p-значение: 0.08 Не получилось отвергнуть нулевую гипотезу, нет оснований считать группы пользователей разными
statistic_check(dataA1, dataB, dataA1PSS, dataBPSS, alpha_bonferroni)
p-значение: 0.22 Не получилось отвергнуть нулевую гипотезу, нет оснований считать группы пользователей разными
Вывод:
во всех четырех группах по событиям была отвергнута нулевая гипотеза, значит у нас нет оснований, считать, что между группами А1 и В есть значимая разница.
statistic_check(dataA2, dataB, dataA2MSA, dataBMSA, alpha_bonferroni)
p-значение: 0.52 Не получилось отвергнуть нулевую гипотезу, нет оснований считать группы пользователей разными
statistic_check(dataA2, dataB, dataA2OSA, dataBOSA, alpha_bonferroni)
p-значение: 0.93 Не получилось отвергнуть нулевую гипотезу, нет оснований считать группы пользователей разными
statistic_check(dataA2, dataB, dataA2CSA, dataBCSA, alpha_bonferroni)
p-значение: 0.59 Не получилось отвергнуть нулевую гипотезу, нет оснований считать группы пользователей разными
statistic_check(dataA2, dataB, dataA2PSS, dataBPSS, alpha_bonferroni)
p-значение: 0.73 Не получилось отвергнуть нулевую гипотезу, нет оснований считать группы пользователей разными
Вывод:
во всех четырех группах по событиям была отвергнута нулевая гипотеза, значит у нас нет оснований, считать, что между группами А2 и В есть значимая разница.
statistic_check(dataA, dataB, dataAMSA, dataBMSA, alpha_bonferroni)
p-значение: 0.35 Не получилось отвергнуть нулевую гипотезу, нет оснований считать группы пользователей разными
statistic_check(dataA, dataB, dataAOSA, dataBOSA, alpha_bonferroni)
p-значение: 0.45 Не получилось отвергнуть нулевую гипотезу, нет оснований считать группы пользователей разными
statistic_check(dataA, dataB, dataACSA, dataBCSA, alpha_bonferroni)
p-значение: 0.19 Не получилось отвергнуть нулевую гипотезу, нет оснований считать группы пользователей разными
statistic_check(dataA, dataB, dataAPSS, dataBPSS, alpha_bonferroni)
p-значение: 0.61 Не получилось отвергнуть нулевую гипотезу, нет оснований считать группы пользователей разными
Вывод:
во всех четырех группах по событиям была отвергнута нулевая гипотеза, значит у нас нет оснований, считать, что между обощенной группой А и группой В есть значимая разница.
Для проведения анализа результатов А/А/В тестирования:
Предполагается, что воронка событий выглядит как: MainScreenAppear -> OffersScreenAppear -> CartScreenAppear -> PaymentScreenSuccessful, при этом больше всего пользователей теряется на 1ом шаге при переходе с главного экрана на страницу с предложениями: остается 61.91% пользователей
Рекомендации: